home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload Trio 2 / Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO / dir24 / jnos110g.zip / BUCKBOOK.C < prev    next >
C/C++ Source or Header  |  1994-04-17  |  37KB  |  1,201 lines

  1. #include <time.h>
  2. #include <sys/timeb.h>
  3. #ifdef MSDOS
  4. #include  <dos.h>
  5. #include <alloc.h>
  6. #endif
  7. #include <ctype.h>
  8. #include <fcntl.h>
  9. #ifdef MSDOS
  10. #include <io.h>
  11. #endif
  12. #include "global.h"
  13. #if defined CALLSERVER || defined ICALL || defined CALLCLI
  14. #include "files.h"
  15. #include "config.h"
  16. #include "socket.h"
  17. #include "cmdparse.h"
  18. #include "ftp.h"
  19.   
  20. /*
  21.     Fred Peachman KB7YW
  22.     7186 Northview Drive
  23.     Brookfield, OH 44403
  24.   
  25.     Fri  05-08-1992
  26.     OK here is my "final" product for the Buckmaster "HamCall" CD-ROM based
  27.     callsign server.
  28.   
  29.    It is a substitute for N4PCR's callsign server. The intent is to replace
  30.    N4PCR's callbook.c with this file - and have a callsign server that will
  31.    run off Buckmaster's CD-ROM: the October 1991 "HAM CALL" CD.(or May  '92)
  32.   
  33.    The CD-ROM must be configured with a driver installed in config.sys,
  34.    and the mscdex.exe TSR must be running.
  35.   
  36.    This module can be compiled one of two ways.
  37.   
  38.     In config.h:
  39.   
  40.    - If CALLSERVER is defined, but ICALL is not:
  41.       you get the same sort of callsign server that I did for the
  42.       October '91 Ham Call CD. It is compatible with either the October
  43.       '91 CD or the May '92 CD. The October '91 CD can utilize the
  44.       Canadian Callsign database as well as the U.S. The May '92 CD does
  45.       not provide the separate Canadian callsign database so the
  46.       "callserver database canadian" command should not be used in
  47.       autoexec.nos. One new feature to this old server is provision for
  48.       "cross-referencing", a feature that is new with May '92 HamCall CD
  49.       but not on the October '91 CD.
  50.   
  51.    - If ICALL is defined, you get the international callsign database
  52.       that is new for the May '92 HamCall CD. The international callsign
  53.       database contains U.S., Canadian and a smattering of foreign callsigns.
  54.       It is based on shelling out to Buckmaster's icall.exe utility which
  55.       is located in the root directory of the May '92 CD. THIS option is
  56.       NOT available on the October '91 HamCall CD!!
  57.   
  58.         To set it up, you use the "cdrom" command to tell NOS the drive
  59.       letter of your CD-ROM drive. You set yourself up as the callserver
  60.       with "callserver hostname <hostname>". You "start callbook". That's it!
  61.   
  62.       - de KB7YW
  63.   
  64.    ---------------------------------------------------------------------------
  65.    - for CALLSERVER:
  66.    Wed  04-29-1992 Mods for the May '92 callbook server. Buckmaster does
  67.    not provide a separate Canadian callsign database on this CD. So this
  68.    code will only be good for the USA database: "s:\\ham0\\hamcall.129".
  69.    "Cross-referencing" works: For a callsign which has changed, the old
  70.    call will be displayed, the date it was changed will be given, and the
  71.    amateur's directory listing under his new callsign will be given. As
  72.    for ICALL, since this is a simple shell to Buckmaster's icall.exe
  73.    utility, I cannot add cross-referencing!
  74.   
  75.    The callserver database commands will configure the cd-rom.
  76.   
  77.    config.h must #define CALLCLI or CALLSERVER. If CALLSERVER is #define'd
  78.    CALLCLI is forced on by config.h. Only CALLCLI can be #define'd separately
  79.    of CALLSERVER so it is the default condition for compilation of this
  80.    module - which won't used at all if neither CALLCLI nor CALLSERVER are
  81.    #define'd in config.h.
  82.  */
  83.   
  84. /* if ICALL is defined, it forces CALLSERVER to be defined. However, if
  85.   CALLSERVER is defined, it does not force ICALL. Therefore definition of
  86.   ICALL controls which server code gets compiled.
  87.   
  88.   If neither CALLSERVER nor ICALL is defined, this module might still be
  89.   needed if CALLCLI is defined, for the callserver client code.
  90.   
  91.  */
  92. struct months {
  93.     char *m;  /* name of month  */
  94.     int  d;   /* days in month  */
  95. };
  96.   
  97. #ifdef ICALL
  98. #ifndef CALLSERVER
  99. #define CALLSERVER 1
  100. #endif    /* #ifndef CALLSERVER */
  101. #endif    /* #ifdef ICALL */
  102.   
  103. char  *Callserver = NULLCHAR;
  104. static int
  105. docallserver_hostname(int argc,char *argv[],void *p);
  106.   
  107. #ifdef CALLSERVER
  108. static int
  109. docallserver_databases(int argc,char *argv[],void *p);
  110.   
  111. /* CD-ROM code by Fred Peachman KB7YW */
  112. char *CDROM = NULLCHAR;
  113. char *cdrom_prompt =
  114. "\tUse the \"cdrom\" command to specify CD-ROM's drive letter\n";
  115. struct months month[12] = {
  116.     {"January", 31},{"February",28},{"March",31},{"April",30},{"May",31},
  117.     {"June",30},{"July",31},{"August",31},{"September",30},{"October",31},
  118.     {"November",30},{"December",31}
  119. };
  120.   
  121. #ifndef ICALL
  122. /* What follows is the original stuff for the Oct '91 Ham Call CD:  */
  123.   
  124. /*struct database_activity
  125.   One of these structs gets filled out by init -
  126.   initializes data base for calls from hamcall.c -
  127.   should be able to handle generic functions across different data base
  128.   formats.
  129.  */
  130. struct database_activity {
  131.     int type;                     /* for checking data/function formats*/
  132. #define USA 0
  133. #define CANADA 1
  134.     int  handle;                  /* -1 if unitialized  */
  135.     unsigned long record;         /* current record #                 */
  136.     void *record_buf;           /* where to read the record to      */
  137. };
  138.   
  139. /* The Buckmaster HAM CALL CD format for U.S. Callsign database       */
  140. struct usa  {
  141.     char callsign[6];   /* None of these fields are ASCIIZ so be careful */
  142.     char lastname[24];
  143.     char firstname[11];
  144.     char middle[1];
  145.     char street[35];
  146.     char city[20];
  147.     char state[2];
  148.     char zip[5];
  149.     char fluff[8];      /* supreme nothingness                            */
  150.     char exp[2];        /* 'X3' if license expired                        */
  151.     char born[2];       /* Last two digits of year of birth  i.e. '54     */
  152.     char expiryr[2];    /* Last two digits of year of license expiration  */
  153.     char expirday[3];   /* The day of the year that license expires       */
  154.     char validyr[2];    /* year that license was issued                   */
  155.     char validday[3];   /* day of the year that license was issued        */
  156.     char class[1];      /* 1 letter license classification                */
  157.     char endline[2];    /* file marker                                    */
  158. };
  159.   
  160. /* The Buckmaster HAM CALL CD format for Canadian Callsign database
  161.    It is 0x9a bytes long (154) The first record has a blank callsign field
  162.    that should be no problem!                                             */
  163.   
  164. struct canada  {
  165.     char callsign[8];
  166.     char name[0xe8 - 0xa2];
  167.     char street[0x10a - 0xe8];
  168.     char town[0x121-0x10a];
  169.     char province[0x12c-0x121];
  170.     char zip[6];
  171.     char endline[2];
  172. };
  173.                     /* generic function calls: headers */
  174. static int
  175. init(struct database_activity *db,int type,int s);
  176. static int
  177. close_down(struct database_activity *db);
  178. static void
  179. disp(char *cfield, int siz, int s);
  180. static int
  181. readrec(struct database_activity *db);
  182. static int
  183. displayrec(struct database_activity *db, int s);
  184. static int
  185. find_call(struct database_activity *db, char *callsign);
  186. static int
  187. make_mask(char *mask, char *callsign, int s);
  188.   
  189.                         /* for u.s.a database:  */
  190. static int
  191. display_usa(void *,int s);
  192. static int
  193. comp_usa_calls(char *, char *);
  194.   
  195.                         /* for canada database  */
  196. static int
  197. display_canada(void *,int s);
  198. static int
  199. comp_canada_calls(char *, char *);
  200.   
  201. /* most of this module is server code, but some is client: CALLCLI is by
  202.    default - forced on in config.h whenever CALLSERVER is #define'd */
  203.   
  204. /* How many different databases are we supporting right now?              */
  205. #define DATABASE_TYPES 2
  206.   
  207. /* how many passes should we make before we give up (in find_call)?    */
  208. #define MAXREADS 30
  209. /*----------------------------------------------------------------------------
  210.   
  211.     Here are arrays of variables to use depending on which database is
  212.     needed. In each array make sure the value order is same as defs for
  213.     database_activity.types i.e. USA, CANADA, ....
  214.   
  215.     In order to tack on another database, add an element for each of these
  216.     arrays, increment the definition for DATABASE_TYPES (above), and
  217.     tell your program how to tell init to use the new one.  In this
  218.     module the launcher (cb_lookup) will figure out which database to
  219.     initialize ("init(type, callsign,s)").
  220.   
  221.     Basically for the new database you would only need to specify its file
  222.     name in database[], write a display function and specify it in
  223.     displayfunc[], specify the size of one record in record_size[],
  224.     write a callsign comparison function and specify it in compare_calls[],
  225.     and specify the byte offset of the callsign field within a record -
  226.     specify this offset in call_offset[] - it will probably be 0 anyway.
  227.   
  228.     You need to write something in cb_lookup to tell init when to initialize
  229.     your new database. Basically if "call" satisfies inspection, initialize
  230.     the appropriate database.
  231.  */
  232.   
  233. char *database[DATABASE_TYPES]  = {
  234.     { NULLCHAR  }, /* "S:\\HAM0\\HAMCALL.129"},  USA  */
  235.     { NULLCHAR  }  /* "S:\\HAM0\\CANADA2.DAT"}   CANADA */
  236. };
  237.   
  238. char *database_label[DATABASE_TYPES]  = {
  239.     { "usa"},{"canadian"}
  240. };
  241.   
  242. int (*displayfunc[DATABASE_TYPES])(void *, int) = {
  243.     { display_usa },
  244.     { display_canada }
  245. };
  246.   
  247. /* basic record size, in bytes, in each database  */
  248. int record_size[DATABASE_TYPES] =   {   129, 154  };
  249.   
  250.             /* How fresh is this stuff in the database?
  251.             Wed  04-29-1992  Now handled by getftime  - KB7YW
  252. static char *Volume = "October 1991";
  253.               */
  254.   
  255.             /* number of records in each type of database */
  256. unsigned long records[DATABASE_TYPES];
  257.   
  258.        /* The database-specific callsign comparison procedure  */
  259. int (*compare_calls[DATABASE_TYPES])(char *,char *) = {
  260.     {comp_usa_calls},{comp_canada_calls}
  261. };
  262. /*      At what offset within a database struct is the callsign located?
  263.                Look at struct usa and struct canada above
  264.  */
  265. int call_offset[DATABASE_TYPES] = { 0,0 };
  266.   
  267. /*
  268. char *BusyServer = "\n\nThe callsign server is busy right now\n\
  269. .... please call back later\n\n";
  270.  */
  271.   
  272. char *Db_Unavail = "\n\nDatabase is not available.\n";
  273.   
  274. char *Db_Not_config = "\n\nDatabase is not configured.\n";
  275.   
  276. static char *TAB = "        ";
  277.   
  278. /*--------------------------------------------------------------------------*/
  279. /* --------------- Generic (database-independent) functions ----------------*/
  280. /*------------------------------------------------------------------------
  281.   Initialize a database activity struct. this consists of making sure
  282.   the database file is opened and that the number of records in that
  283.   file is computed. Also a record_buf is malloced for each db.
  284.  */
  285. static int
  286. init(db, type, s)
  287. struct database_activity *db;
  288. int type;
  289. int s;
  290. {
  291.     long tmp;
  292.     int i;
  293.     extern char *Callserver;  /* declared in calldbd.c  */
  294.     if ((type < 0) || (type >= DATABASE_TYPES))  return -1;
  295.   
  296.     db->type = type;
  297.   
  298.     if (database[type] == NULLCHAR)  {
  299.         usputs(s,Db_Not_config);
  300.         return -1;
  301.     }
  302.   
  303.     i = db->handle = _open(database[type], O_RDONLY | O_BINARY);
  304.     if (i == -1) {
  305.         usputs(s,Db_Unavail);
  306.         return -1;
  307.     }
  308.     for (i = 0; i < 50; i++)  pwait(NULL);
  309.   /* The CD-ROM is a slow device!! - give other processes lots of slack */
  310.   
  311.     tmp = filelength(db->handle);
  312. /*   if (tmp == -1L)  {
  313.     usprintf(s,"Error reading length of file \"%s\"\n\
  314.     please report to the sysop at %s\n",database[type], Callserver);
  315.     return -1;
  316.   }
  317. */
  318.     records[type] = tmp/((long) record_size[type]);
  319.   
  320.     db->record_buf = (char *)malloc(record_size[type]);
  321.     if (db->record_buf == NULL) {
  322.         usputs(s,Db_Unavail);  /* No memory to allocate, actually  */
  323.         return -1;
  324.     }
  325.     return 0;
  326. }
  327.   
  328. static int
  329. close_down(db)
  330. struct database_activity* db;
  331. {
  332.     free(db->record_buf);
  333.     _close(db->handle);
  334.     return 0;
  335. }
  336. static int
  337. invalid_callsign(callsign, s)
  338. char *callsign;
  339. int s;
  340. {
  341.     usprintf(s,"\n\n\"%s\" is an invalid callsign\n\n", callsign);
  342.     return -1;
  343. }
  344.   
  345. /* make_mask makes a Buckmaster-compatible mask out of the callsign.
  346.   caller must allocate 6 bytes exactly for "mask" or 7 to make an ascii
  347.   string out of it.
  348.   
  349.   callsign is the callsign we are looking to match; mask is the same
  350.   callsign only padded to Left with a space if necessary to keep the
  351.   callsign digit at position 2 (starting with 0), and padded with spaces
  352.   to the right if necessary to make sure the mask is exactly 6 digits
  353.   long.
  354.   
  355.   This routine will work right for US or Canadian callsigns, but NOT with
  356.   foreign callsigns (that contain more than one numeric character) it
  357.   will fail!
  358.  */
  359.   
  360. static int
  361. make_mask(mask, callsign,s)
  362. char *mask;
  363. char *callsign;
  364. int s;
  365. {
  366.     int j, k, len, already=0;
  367.     char *q, *p;
  368.     char tmp[7];
  369.   
  370.     len = strlen(callsign);
  371.   
  372.   /* there must be only ONE digit-character at position 1 or 2
  373.       (starting from 0)  */
  374.     q = callsign;
  375.     already = 0;
  376.     for (j = 0; j < len; j++)   {
  377.         if (isdigit(*q++))  {
  378.             if (!(already++)) k = j;/* already counts the number of digit chars */
  379.             else return invalid_callsign(callsign,s);
  380.         }
  381.     }
  382.     if ((k < 1) || ( k > 2))  return invalid_callsign(callsign,s);
  383.   
  384. /* FINALLY, Buckmaster pads to the left with spaces to position the digit at
  385.   mask[2] */
  386.   
  387.     q = mask;
  388.     for (j = 0; j < 6; j++) *q++ = ' '; /* initialize!  */
  389.   
  390.     p = callsign;
  391.     q = mask;
  392.   
  393.     switch (k)  {
  394.         case 1:
  395.             *q++ = ' '; /* left-pad with a space  */
  396.             break;
  397.   
  398.         case 2:
  399.             break;
  400.   
  401.         default:
  402.             break;
  403. /*  usprintf(s,"Error in make_mask %s line %d\n",__FILE__,__LINE__);
  404.     exit(255);
  405. */
  406.     }
  407.   
  408.     for (j = 0; j < len; j++) *q++ = *p++;
  409.   
  410.     return ((int) (mask[2]) &0x0f); /* returns digit of the valid U.S. callsign */
  411. }
  412.   
  413. /* a method for display of non-ASCIIZ strings */
  414. static void
  415. disp(cfield,siz,s)
  416. char *cfield;
  417. int siz;
  418. int s;
  419. {
  420.     char *q;
  421.     int i, sw=0;
  422.   
  423.     q = cfield;
  424.     for (i = 0; i < siz; i++) {
  425. /* Was:
  426.     if ((!sw) || (*q != ' ')) usputc(s, (int) *q, stdout);
  427.     Now is:  (WG7J) */
  428.         if ((!sw) || (*q != ' ')) usputc(s, (int) *q);
  429.         if (*q == ' ') sw = 1;
  430.         else sw = 0;  /* We will print out one space before we quit!  */
  431.         q++;
  432.     }
  433. }
  434. readrec(db)
  435. struct database_activity *db;
  436. {
  437.     unsigned long filepointer;
  438.     int n;
  439.     int st;
  440.   
  441.     st = db->type;
  442.     filepointer = db->record*((long) record_size[st]);
  443.     lseek(db->handle, filepointer,SEEK_SET);
  444.     n = _read(db->handle, db->record_buf,record_size[st]);
  445.     if (n != -1) return 0;
  446.     else return -1;
  447. }
  448.   
  449. static int
  450. displayrec(db,s)
  451. struct database_activity *db;
  452. int s;
  453. {
  454.     int (*func)(void *v, int s);
  455.   
  456.     func = displayfunc[db->type];
  457. /*   usprintf(s,"\n%s*** Record # % 8lu:***\n",TAB, db->record);  */
  458.     return func(db->record_buf,s);
  459. }
  460. /* find_call: Find the record number that matches the callsign. For any
  461.    callbook data where the records are all of the same length. The
  462.    returned record value is found as db->record.
  463.   
  464.    If the input callsign needs to be converted to a search mask in any
  465.    way, do it then call findcall.
  466.   
  467.    The returned int value is the number of records read during the search
  468.    process.
  469.  */
  470.   
  471. static int
  472. find_call(db, callsign)
  473. struct database_activity *db;
  474. char *callsign;
  475. {
  476.     int i, ret, met= 0, reads = 0;
  477.     long ra,rz;  /* first, middle, last record numbers */
  478.     int (*comp)(char *, char *);
  479.     int st;
  480.     char *rec_call;
  481.   
  482.     st = db->type;  /* determine database structure type  */
  483.     comp = compare_calls[st];
  484. /* set initial pointers */
  485.     ra = 0L;
  486.     rz = records[st] - 1L;
  487.     db->record = (ra + rz)/2L;  /* go for the middle  */
  488.   
  489.     rec_call = (char *)db->record_buf + call_offset[st];
  490.     ret = 1;
  491.     while (ret != 0)  {
  492.         readrec(db);  /* read in the data pointed to by db->record  */
  493.         for (i=0; i < 50; i++)
  494.             pwait(NULL);  /* sit back and let other processes run for a while.
  495.       The CD-ROM is a slow-access device so lots of other processes may
  496.       be waiting or trying to run - like the keyboard!!! */
  497.   
  498.         reads++;
  499.         ret = comp(callsign, rec_call);
  500.   
  501.         if (ret < 0)  /* callsign < record's callsign field  */
  502.             rz = db->record - 1L;
  503.         else if (ret > 0)  /* callsign > record's callsign field  */
  504.             ra = db->record + 1L;
  505.         else return reads;
  506.   
  507.         if (reads == MAXREADS) return -1;  /* Abnormal return  */
  508.         else if (ra >= rz)  {
  509.             if (met)  return -1;
  510.             else  met = 1;  /* catch this if it happens a second time!  */
  511.         }
  512.         db->record = (ra + rz)/2L;
  513.     }
  514.   /* not reached  */
  515. }
  516. /*---------------- ROUTINES FOR U.S. CALLSIGN DATABASE ----------------------*/
  517.   
  518. /* getdaynum: given a character array 'p' of 'siz' elements, convert the
  519.    array to an ASCIIZ string, return the integer value that it
  520.    represents.
  521.  */
  522. static int
  523. getdaynum(p,siz)
  524. char *p;
  525. int siz;
  526. {
  527.     char *q, tmp[80];
  528.     int ret, i;
  529.   
  530.     q = p;
  531.     for (i = 0; i < siz; i++) {
  532.         tmp[i] = *q++;
  533.     }
  534.     tmp[i] = '\0';
  535.   
  536.     ret = atoi(tmp);
  537.   
  538.     return ret;
  539. }
  540. /* yrval:
  541.    Take the two-letter year code and determine if we have a year that is in
  542.    the 1900's or the 21st century. Return a proper integer answer:
  543.    i.e. "2002" or "1998".
  544.  */
  545. static int
  546. yrval(yrin)
  547. char *yrin;
  548. {
  549.     int i, num;
  550.   
  551.     num = getdaynum(yrin, 2);
  552.   
  553.     if (num > 90) num += 1900;
  554.     else num += 2000;
  555.     return num; /* an integer */
  556. }
  557. /* day_calc: given the 3-element character array 'day' and the 2-element
  558.    character array 'yr', construct a string that returns the date for
  559.    that year, in the form "February 28, 2002" */
  560.   
  561. static char *
  562. day_calc(day, daystr, yr)
  563. char day[3];
  564. char *daystr;
  565. char yr[2];
  566. {
  567.     int i, daynum, subt, year, t;
  568.     char *q, tmp[4];
  569.     int leap = 0;
  570.   
  571.     year = yrval(yr);
  572.   
  573.     if (!(year%4))  leap = 1; /* evenly divisible by four means leap year */
  574.   
  575.     daynum = getdaynum(day, 3);
  576.     if (daynum == 0)  return NULL;
  577.   
  578.   
  579.     subt = 0;
  580.     for (i = 0; i < 12; i++)  {
  581.         if (((t = subt + ((i == 1) ? leap : 0) + month[i].d)) > daynum) break;
  582.         else (subt = t);
  583.     }
  584.     t = daynum - subt;
  585.     if (i == 12)  daystr[0] = '\0';
  586.     else  sprintf(daystr,"%s %d, %d", (t > 0 ? month[i].m : month[--i].m),
  587.         (t > 0 ? t : month[i].d + ((i==1) ? leap : 0)),
  588.         year);
  589.     return daystr;
  590. }
  591. /* comp_usa_calls compares the sought-for callsign with a mask of the
  592.    type encountered in the callsign field of a struct usa,
  593.   
  594.   returns 0 if they match, -1 if mask1 < mask2, 1 if mask1 > mask2.
  595.   mask1 is the callsign requested, mask2 is the database callsign from
  596.   the record under test for match.
  597.  */
  598. static int
  599. comp_usa_calls(mask1, mask2)
  600. char mask1[6];
  601. char mask2[6];
  602. {
  603.     int i;
  604.     char *p, *q;
  605.   
  606.   /* This is in accordance with the way that Buckmaster orders callsigns
  607.      on its CD-ROM database, so follow along:
  608.    */
  609.   
  610.     p = &mask1[2], q = &mask2[2];
  611.     for (i =2; i < 6; i++)  {
  612.         if (*p != *q)  return (int) *p - (int) *q;
  613.         p++, q++;
  614.     }
  615.     p = mask1, q = mask2;
  616.     for (i =0; i < 2; i++)  {
  617.         if (*p != *q)  return (int) *p - (int) *q;
  618.         p++, q++;
  619.     }
  620.     return 0;
  621. }
  622.   
  623. /* for display of an 'X' record in a US callsign record Apr '92 HamCall CD.
  624.   first three elements of the usa struct look like this:
  625.   
  626.        callsign: ∙N8MVI
  627.        lastname: ∙CHANGED∙08/27/91∙∙∙∙∙∙∙
  628.       firstname: ∙SEE∙KF8PH∙
  629.   
  630.       where '∙' is a placeholder to show size only.
  631.       Let's do an appropriate display of this information.
  632.   */
  633.   
  634. static int
  635. display_x(us,s)
  636. struct usa *us;
  637. int s;
  638. {
  639. /*
  640.   in us->lastname, ["∙CHANGED∙08/27/91∙∙∙∙∙∙∙"] us->lastname[17] is where
  641.   to ASCIIZ-ize it. us->callsign is not NULL-terminated, and us->lastname
  642.   follows it immediately. So we will just print out the first two elements
  643.   of the structure usa:
  644.  */
  645.     us->lastname[17] = '\0';
  646. /* A NULL at us->middle will ASCIIZ-ise us->firstname, of which we want
  647.   all past the "SEE".
  648.  */
  649.     us->middle[0] = '\0';
  650.     usprintf(s,"%s\nNew callsign is \"%s\"\n\n", us->callsign, &us->firstname[5]);
  651.     return 0;
  652. }
  653. /*
  654.     Old U.S. callsign display format:
  655.     Buckmaster CD-ROM October 1991
  656.       *** Record #   452100:***
  657. Call:   KB7YW
  658.         FREDERICK A PEACHMAN
  659.         7186 NORTHVIEW DR
  660.         BROOKFIELD , OH 44403
  661. Born:   1954
  662. Class:  EXTRA
  663. Exp:    March 29, 1998
  664.  */
  665. static int
  666. display_usa(us,s)
  667. void *us;
  668. int s;
  669. {
  670.     struct usa *u;
  671.     char daystr[20];
  672.     int yeardate;
  673.     char *Unknown = "%sUnknown Expiration Date\n";
  674.   
  675. /* Sat  05-02-1992  Here I am inserting code to recognize license class 'X',
  676.   which is Buckmaster's way of saying that the license has expired because
  677.   the licensee has changed his callsign - for whatever reason. This is only
  678.   a possibility with the USA database and then only for CD's dated april
  679.   '92 or later. See 'display_x()', above.
  680.     I have commented out my old display format. Gonna simplify it
  681.   somewhat - KB7YW */
  682.   
  683.     u = (struct usa *)us;
  684.   
  685.     if (u->class[0] == 'X')  {
  686.         display_x(us,s);
  687.         return -2;  /* -2 is a flag for the caller to recognize an 'X' record */
  688.     }
  689. /*   usprintf(s, "CALL:   "); */
  690.     disp(u->callsign,sizeof(u->callsign),s);
  691. /*   usprintf(s,"\n%s", TAB); */
  692.     usputc(s,'\n');
  693.     disp(u->firstname,sizeof(u->firstname),s);
  694.     disp(u->middle,sizeof(u->middle),s);
  695.     usputc(s,' ');
  696.     disp(u->lastname,sizeof(u->lastname),s);
  697. /*   usprintf(s,"\n%s", TAB); */
  698.     usputc(s,'\n');
  699.     disp(u->street,sizeof(u->street),s);
  700. /*   usprintf(s,"\n%s", TAB); */
  701.     usputc(s,'\n');
  702.     disp(u->city,sizeof(u->city),s);
  703.     usputs(s,", ");
  704.     disp(u->state,sizeof(u->state),s);
  705.     usputc(s,' ');
  706.     disp(u->zip,sizeof(u->zip),s);
  707.     if (u->born[0] != '-')  {
  708.         usputs(s,"\nBorn 19");
  709.         disp(u->born,sizeof(u->born),s);
  710.     }
  711.     usputs(s,"\nClass:  ");
  712.   
  713.     switch(u->class[0])   {
  714.         case 'N':
  715.             usputs(s,"NOVICE");
  716.             break;
  717.   
  718.         case 'T':
  719.             usputs(s,"TECHNICIAN");
  720.             break;
  721.   
  722.         case 'C':
  723.             usputs(s, "CLUB STATION");
  724.             break;
  725.   
  726.         case 'G':
  727.             usputs(s,"GENERAL");
  728.             break;
  729.   
  730.         case 'A':
  731.             usputs(s,"ADVANCED");
  732.             break;
  733.   
  734.         case 'E':
  735.             usputs(s,"EXTRA");
  736.             break;
  737.   
  738.         default:
  739.             usputc(s,u->class[0]);
  740.     }
  741.     usputc(s,'\n');
  742.   
  743.     yeardate = yrval(u->expiryr);
  744.     if (day_calc(u->expirday, daystr, u->expiryr) != NULL)  {
  745.         if (daystr[0] != '\0')  {
  746.             if ((u->exp[0] == ' ') || (yeardate > 2000))  {
  747.                 usputs(s,"Expires ");
  748.                 usprintf(s,"%s\n", daystr);
  749.             }
  750.             else usprintf(s,Unknown,TAB);
  751.         }
  752.         else usprintf(s,Unknown,TAB);
  753.     }
  754.     return 0;
  755. }
  756.   
  757. /*---------- Procedures specific to Canadian Callsign DataBase----------------*/
  758. static int
  759. display_canada(us,s)
  760. void *us;
  761. int s;
  762. {
  763.     struct canada *u;
  764. #define DISP(x) {usputs(s,"\n        ");\
  765. disp(x, sizeof(x),s);}
  766.   
  767. u = (struct canada *) us;
  768.   
  769. DISP(u->callsign);
  770. DISP(u->name);
  771. DISP(u->street);
  772. DISP(u->town);
  773. disp(u->zip, sizeof(u->zip),s);
  774. usputc(s,'\n');
  775. usprintf(s,"%sCANADA\n",TAB);
  776.   
  777. return 0;
  778. }
  779. /* comp_canada_calls compares the sought-for callsign with a mask of the
  780.    type encountered in the callsign field of a struct canada,
  781.   
  782.   returns 0 if they match, -1 if mask1 < mask2, 1 if mask1 > mask2.
  783.   mask1 is the callsign requested, mask2 is the database callsign from
  784.   the record under test for match.
  785.  */
  786.   
  787. static int
  788. comp_canada_calls(mask1, mask2)
  789. char mask1[6];
  790. char mask2[6];
  791. {
  792.     int i;
  793.     char *p, *q;
  794.   
  795.     p = mask1, q = mask2;
  796.     for (i =0; i < 6; i++)  {
  797.         if (*p != *q)  return (int) *p - (int) *q;
  798.         p++, q++;
  799.     }
  800.     return 0;
  801. }
  802. /* One of three public  functions!  */
  803. int
  804. cb_lookup(s,call)
  805. int s;
  806. char  *call;
  807. {
  808. /* This is the launcher.  Get rid of any callsign validity-checking code
  809.   that happens before calling cb_lookup. Eventually we will have to
  810.   launch the US, Canadian, or foreign Buckmaster CD databases. For right
  811.   now, tho, we only have USA and CANADA.
  812.   
  813.   For April '92 CD we only have USA! - KB7YW
  814.   
  815.     Sat  05-02-1992  Here I am inserting code to recognize license class 'X',
  816.   which is Buckmaster's way of saying that the license has expired because
  817.   the licensee has changed his callsign - for whatever reason. This is only
  818.   a possibility with the USA database and then only for CD's dated april
  819.   '92 or later - KB7YW
  820.  */
  821.     char *q;
  822.     int len, j;
  823.     char mask[7];
  824.     struct database_activity DA;
  825.     char *cant = "Can't handle callsign \"%s\"\n";
  826.     struct ftime ft;
  827.     struct usa *u;
  828.   
  829.                   /* FIRST TEST CALLSIGN FOR VALIDITY */
  830.   
  831.   /* Buckmaster only matches callsigns of 4 to 6 letters in length  */
  832.     len = strlen(call);
  833.     if ((len < 4) || (len > 6)) return invalid_callsign(call,s);
  834.   
  835.     for (j = 0; j < 6; j++) call[j] = toupper(call[j]);
  836.   
  837.                   /* every character must be alphanumeric */
  838.     q = call;
  839.     for (j = 0; j < len; j++) if (!(isalnum(*q++)))
  840.             return invalid_callsign(call,s);
  841.   
  842.     q = call;
  843.         switch (*q) {
  844.             case 'A':
  845.             case 'K':
  846.             case 'N':
  847.             case 'W':
  848.                 if ((init(&DA, USA, s)) == -1)  return 0;
  849.                 if ((make_mask(mask, call, s)) == -1) {
  850.                     close_down(&DA);
  851.                     return 0;
  852.                 }
  853.                 break;
  854.   
  855.             case 'V':
  856.                 if ((strncmp(call,"VO",2) == 0) || (strncmp(call,"VE",2) == 0)) {
  857.                     if ((init(&DA, CANADA, s)) == -1)  return 0;
  858.                     if ((make_mask(mask, call, s)) == -1)   {
  859.                         close_down(&DA);
  860.                         return 0;
  861.                     }
  862.                 }
  863.                 else  {
  864.                     usprintf(s,cant,call);
  865.                     return 0;
  866.                 }
  867.                 break;
  868.   
  869.             default:
  870.                 usprintf(s,cant,call);
  871.                 return 0;
  872.         }
  873.   
  874.     if ((find_call(&DA,mask)) == -1)
  875.         usprintf(s, "\n\nCallsign \"%s\" not found\n\n",call);
  876.     else {
  877.         while (displayrec(&DA,s) == -2)  {
  878. /* If we got to here, we are dealing with an 'X' record, which is only a
  879.   possibility in the USA database. (Of course now (Sat  05-02-1992), we
  880.   only have the U.S. database to play with as of April '92 HamCall CD).
  881.   Soon we can get rid of the Canadian stuff, since the CD's that support
  882.   it will be too old to use.
  883.  */
  884.             u = (struct usa *) DA.record_buf;
  885.   
  886. /* Buckmaster will have filled out u->firstname as: "∙SEE∙KF8PH∙". So
  887.    our new callsign to look up is at position 5 in u->firstname!
  888.  */
  889.             strncpy(call, &u->firstname[5], 6);/* put in the new callsign !!! */
  890.             if ((make_mask(mask, call, s)) == -1)   {
  891.                 close_down(&DA);
  892.                 return 0;
  893.             }
  894.             if ((find_call(&DA,mask)) == -1)  {
  895.                 usprintf(s, "\n\nCallsign \"%s\" not found\n\n",call);
  896.                 break;  /* Get out of what might otherwise be an infinite loop  */
  897.             }
  898.         }
  899.         getftime(DA.handle, &ft);
  900.         usprintf(s,"\nDatabase date: %s %d, %02d\n\n\n",
  901.         month[ft.ft_month-1].m, ft.ft_day, ft.ft_year+1980);
  902.     }
  903.     /* find_call returns with appropriate record already read into
  904.        DA.record_buf and the return value is -1 for error or number of
  905.        seeks required on success.
  906.      */
  907.   
  908.     close_down(&DA);
  909.     return 0;
  910. }
  911.   
  912. #else   /* #ifndef ICALL  i.e. ICALL is now defined, & here's what you get: */
  913.   
  914. /* Buckmaster's international callsign database - new April '92             */
  915. long Mincoreleft = 77000L;      /* minimum coreleft needed for icall.exe    */
  916. int init_Database(char *);      /* public - used by cdbstart in callcdb.c   */
  917. static int get_Database_date(char *);
  918. char *Database_date = NULLCHAR; /* set once in get_Database_date()          */
  919. char *Database = NULLCHAR;      /* set by init_Database()                   */
  920. char *db = "/ham0/hamcall.all"; /* default initialized address for Database */
  921.   
  922. int
  923. cb_lookup(s,call)
  924. int s;
  925. char  *call;
  926. {
  927.     FILE *fp, *fd;
  928.     char command[50];
  929.     char file[20], dosfile[20];
  930.     int fhandle, retval;
  931.     long filesize;
  932.     char *q, *p;
  933.   
  934.     if (coreleft() < Mincoreleft) {
  935.         usprintf(s,
  936.         "\n\n\tLow System Memory !!!.\n\
  937.         \tAlert sysop @ %s:\n\
  938.         \tCannot service callsign -\n\
  939.         \tdatabase requests until\n\
  940.         \tprogram reboots.\n\n", Callserver);
  941.         log(s,"callserver failure [insufficient memory]");
  942.         return 0;
  943.     }
  944.   
  945.     if (Database == NULLCHAR)
  946.         init_Database(NULLCHAR);
  947.   
  948.     tmpnam(file);
  949.     strcpy(dosfile, file);
  950.     q = dosfile;
  951.   
  952.     while (*q != '\0')  {
  953.         if (*q == '/') *q = '\\'; /* make slashes into backslashes for MS-DOS */
  954.         q++;
  955.     }
  956.   
  957.     sprintf(command,"%s\\icall.exe %s %s *> %s",CDROM, CDROM, call, dosfile);
  958.   
  959.     retval = system(command);
  960.   
  961.     if (!retval)  {
  962.         if((fp = fopen(file,READ_TEXT)) != NULLFILE) {
  963.             sendfile(fp,s,ASCII_TYPE,0,NULL);
  964.             if (Database_date != NULLCHAR)
  965.                 usprintf(s, "\n\nDatabase date: %s\n\n\n", Database_date);
  966.             fclose(fp);
  967.             remove(file);
  968.             return 0;
  969.         }
  970.     }
  971.     usputs(s,"Could not open database - please try later\n\n");
  972.     return 0;
  973. }
  974. #endif  /* #ifndef ICALL */
  975. #endif /* #ifdef CALLSERVER */
  976.   
  977. struct cmds Callserver_cmds[] = {
  978.     "hostname", docallserver_hostname, 0,  0,  NULLCHAR,
  979. #ifdef CALLSERVER     /* not used if only CALLCLI is defined  */
  980.     "database", docallserver_databases,  0,  0,  NULLCHAR,
  981. #endif
  982.     NULLCHAR,
  983. };
  984.   
  985. /* Another public, headered in commands.h */
  986. int
  987. docallserver(argc,argv,p)
  988. int argc;
  989. char *argv[];
  990. void *p;
  991. {
  992.     return subcmd(Callserver_cmds, argc, argv, p);
  993. }
  994.   
  995. static int
  996. docallserver_hostname(argc,argv,p)
  997. int argc;
  998. char *argv[];
  999. void *p;
  1000. {
  1001.     if(argc < 2)
  1002.     {
  1003.         if(Callserver != NULLCHAR)
  1004.             tprintf("The callserver's hostname is: %s\n",Callserver);
  1005.         else
  1006.         {
  1007.             tputs("Callserver not configured!\n");
  1008.             tputs("Usage: callserver <hostname>|<ip_address>\n");
  1009.         }
  1010.     }
  1011.     else {
  1012.         if(Callserver != NULLCHAR)
  1013.             free(Callserver);
  1014.         Callserver = strdup(argv[1]);
  1015.     }
  1016.     return 0;
  1017. }
  1018. #ifdef CALLSERVER
  1019. #ifdef ICALL
  1020.   
  1021. /* For the new (May '92) HamCall CD the  callserver database is
  1022. "/ham0/hamcall.all" unless the user specifies otherwise
  1023.  */
  1024.   
  1025. static int
  1026. get_Database_date(db)
  1027. char *db; /* temporary string contains putative database filename  */
  1028. {
  1029.     FILE *fd;
  1030.     int fhandle;
  1031.     struct ftime ft;
  1032.     char tmpstr[50];
  1033.     char database[50];
  1034.   
  1035. /*  CDROM contains drive letter + ':', e.g. "s:"
  1036.     db contains the rest of the path + database file name, e.g.
  1037.     "/ham0/hamcall.exe".
  1038.  */
  1039.     sprintf(database, "%s%s", CDROM, db);
  1040.   
  1041.     if ((fd = fopen(database, "rb")) != NULLFILE) {
  1042.         fhandle = fileno(fd);
  1043.         getftime(fhandle, &ft);
  1044.         sprintf(tmpstr,"%s %d, %02d",
  1045.         month[ft.ft_month-1].m, ft.ft_day, ft.ft_year+1980);
  1046.         fclose(fd);
  1047.         if (Database_date != NULLCHAR) free(Database_date);
  1048.         Database_date = strdup(tmpstr); /* Now it's initialized!! */
  1049.         return 0;
  1050.     }
  1051.     else return -1;   /* Database could not be opened for binary read */
  1052. }
  1053.   
  1054. /* public: called when the ICALL callserver is started. db must be allocated
  1055.     with strdup unless it is to be assigned to the global char *db,
  1056.     declared this module. In which case db should be passed as NULLCHAR.
  1057.  */
  1058.   
  1059. int
  1060. init_Database(tmp_db)
  1061. char *tmp_db;
  1062. {
  1063.     extern char *db;  /* global: "/ham0/hamcall.all"  */
  1064.   
  1065.     if (tmp_db == NULLCHAR) tmp_db = db;
  1066.   
  1067.     if (Database == NULLCHAR)
  1068.         if (CDROM == NULLCHAR)  {
  1069.             tputs(cdrom_prompt);
  1070.             return -1;
  1071.         }
  1072.     if (get_Database_date(tmp_db) == 0) {
  1073.         Database = tmp_db;
  1074.         return 0;
  1075.     } else  {
  1076.         tprintf(
  1077.         "\t%c%s could not be found.\n\
  1078.         \tPlace \"Ham Call\" CD in CD-ROM drive \"%s\" -OR\n",'\007',tmp_db, CDROM);
  1079.         tputs(
  1080.         "\tspecify alternate callserver database filename.\n");
  1081.         return -1;
  1082.     }
  1083. }
  1084.   
  1085. static int
  1086. docallserver_databases(argc,argv,p)
  1087. int argc;
  1088. char *argv[];
  1089. void *p;
  1090. {
  1091.     char *tmpstr;
  1092.     char *cs = "callserver database";
  1093.   
  1094.     if (argc == 1 ) {
  1095.         tprintf("%s ", cs);
  1096.         if (Database == NULLCHAR) {
  1097.             tputs("not configured.\n"); /* Will not be configured unless
  1098.                                          user has already used the "cdrom"
  1099.                                          command, and optionally
  1100.                                          "callserver database </path/filename>"
  1101.                                          as well as "start callbook".
  1102.                                        */
  1103.             if (CDROM == NULLCHAR)
  1104.                 tputs(cdrom_prompt);
  1105.         }
  1106.         else  tprintf("is \"%s\" (dated %s)\n", Database, Database_date);
  1107.         return 0;
  1108.     } else if (argc >= 2)   {
  1109.         tmpstr = strdup(argv[1]);
  1110.         if (init_Database(tmpstr) == 0) {
  1111.             return 0;
  1112.         } else  {
  1113.             free(tmpstr);
  1114.       /* no error reporting. init_Database() does plenty of that  */
  1115.             return -1;
  1116.         }
  1117.     }
  1118.     return 0;
  1119. }
  1120.   
  1121.   
  1122. #else       /* #ifdef ICALL i.e. ICALL is NOT defined here. You use this
  1123.                 with the old-style database  */
  1124. static int
  1125. docallserver_databases(argc,argv,p)
  1126. int argc;
  1127. char *argv[];
  1128. void *p;
  1129. {
  1130.     int t;
  1131.   
  1132.     if (argc < 2) {
  1133.         for (t = 0; t < DATABASE_TYPES; t++)  {
  1134.             tprintf("callserver database %s ", database_label[t]);
  1135.             if (database[t] == NULLCHAR)  {
  1136.                 tputs("not configured.\n");
  1137.             } else  tprintf("%s\n", database[t]);
  1138.         }
  1139.         return 0;
  1140.     } else  { /* argc >= 2 */
  1141.         for (t = 0; t < DATABASE_TYPES; t++)  {
  1142.             if (strncmp(database_label[t],argv[1],strlen(argv[1])) == 0)  {
  1143.                 if (argc < 3) {
  1144.                     tprintf("callserver database %s ", database_label[t]);
  1145.                     if (database[t] == NULLCHAR)  tputs("not configured\n");
  1146.                     else  tprintf("%s\n", database[t]);
  1147.                 } else  { /* argc >= 3  */
  1148.                     if (database[t] == NULLCHAR)  free(database[t]);
  1149.                     database[t] = strdup(argv[2]);
  1150.                 }
  1151.                 return 0; /* we have found our match with database_label[t] */
  1152.             }
  1153.         }
  1154.       /* no match with database_label: which database?  */
  1155.         tputs("Usage: callserver database ");
  1156.         for (t = 0; t < DATABASE_TYPES; t++)  {
  1157.             tprintf("%s", database_label[t]);
  1158.             if ( t < DATABASE_TYPES - 1)  tputc('|');
  1159.         }
  1160.         tputs(" <database drive:/path/filename>\n");
  1161.         return -1;
  1162.     }
  1163.   /* not reached  */
  1164. }
  1165. #endif  /* #ifdef ICALL  */
  1166.   
  1167. /* the last public of this module, also headered in commands.h  */
  1168. int
  1169. docdrom(argc,argv,p)
  1170. int argc;
  1171. char *argv[];
  1172. void *p;
  1173. {
  1174.     extern char *CDROM; /* declared above, this module  */
  1175.   
  1176.     if (argc < 2) {
  1177.         if (CDROM == NULLCHAR)  {
  1178.             tputs("CDROM drive letter not specified\n");
  1179.         }
  1180.         else  tprintf("CDROM drive is \"%s\"\n", CDROM);
  1181.         return 0;
  1182.     }
  1183.     else  {
  1184.         if ((argv[1][2] == '\0')    &&
  1185.             (isalpha(argv[1][0]))   &&
  1186.         (argv[1][1] == ':'))  {
  1187.             if (CDROM != NULLCHAR)  free(CDROM);
  1188.             CDROM = strdup(argv[1]);
  1189.             return 0;
  1190.         }
  1191.         else  {
  1192.             tprintf("Usage: \"cdrom driveletter:\", e.g. \"cdrom s:\"\n");
  1193.             return 1;
  1194.         }
  1195.     }
  1196.   /* not reached  */
  1197. }
  1198. #endif  /* #ifdef CALLSERVER  */
  1199. #endif /* BUCKBOOK */
  1200.   
  1201.